{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "4be2e6fa-2187-4617-8433-0db4fb0c099c",
   "metadata": {},
   "source": [
    "# LangChain 核心模块学习：Model I/O\n",
    "\n",
    "`Model I/O` 是 LangChain 为开发者提供的一套面向 LLM 的标准化模型接口，包括模型输入（Prompts）、模型输出（Output Parsers）和模型本身（Models）。\n",
    "\n",
    "- Prompts：模板化、动态选择和管理模型输入\n",
    "- Models：以通用接口调用语言模型\n",
    "- Output Parser：从模型输出中提取信息，并规范化内容\n",
    "\n",
    "![](../images/model_io.jpeg)\r\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "2e64b01e-f5ad-4614-b0c3-a140f6bb575a",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Requirement already satisfied: langchain in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (0.0.298)\n",
      "Requirement already satisfied: PyYAML>=5.3 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (6.0)\n",
      "Requirement already satisfied: langsmith<0.1.0,>=0.0.38 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (0.0.39)\n",
      "Requirement already satisfied: dataclasses-json<0.7,>=0.5.7 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (0.5.7)\n",
      "Requirement already satisfied: requests<3,>=2 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (2.31.0)\n",
      "Requirement already satisfied: numpy<2,>=1 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (1.23.5)\n",
      "Requirement already satisfied: tenacity<9.0.0,>=8.1.0 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (8.2.2)\n",
      "Requirement already satisfied: SQLAlchemy<3,>=1.4 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (1.4.41)\n",
      "Requirement already satisfied: numexpr<3.0.0,>=2.8.4 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (2.8.4)\n",
      "Requirement already satisfied: pydantic<3,>=1 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (1.10.8)\n",
      "Requirement already satisfied: async-timeout<5.0.0,>=4.0.0 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (4.0.2)\n",
      "Requirement already satisfied: aiohttp<4.0.0,>=3.8.3 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from langchain) (3.8.5)\n",
      "Requirement already satisfied: multidict<7.0,>=4.5 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (6.0.4)\n",
      "Requirement already satisfied: frozenlist>=1.1.1 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.3.3)\n",
      "Requirement already satisfied: charset-normalizer<4.0,>=2.0 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (3.1.0)\n",
      "Requirement already satisfied: yarl<2.0,>=1.0 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.9.2)\n",
      "Requirement already satisfied: attrs>=17.3.0 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (23.1.0)\n",
      "Requirement already satisfied: aiosignal>=1.1.2 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from aiohttp<4.0.0,>=3.8.3->langchain) (1.3.1)\n",
      "Requirement already satisfied: marshmallow-enum<2.0.0,>=1.5.1 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from dataclasses-json<0.7,>=0.5.7->langchain) (1.5.1)\n",
      "Requirement already satisfied: marshmallow<4.0.0,>=3.3.0 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from dataclasses-json<0.7,>=0.5.7->langchain) (3.19.0)\n",
      "Requirement already satisfied: typing-inspect>=0.4.0 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from dataclasses-json<0.7,>=0.5.7->langchain) (0.9.0)\n",
      "Requirement already satisfied: typing-extensions>=4.2.0 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from pydantic<3,>=1->langchain) (4.6.2)\n",
      "Requirement already satisfied: certifi>=2017.4.17 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from requests<3,>=2->langchain) (2023.5.7)\n",
      "Requirement already satisfied: urllib3<3,>=1.21.1 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from requests<3,>=2->langchain) (1.26.16)\n",
      "Requirement already satisfied: idna<4,>=2.5 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from requests<3,>=2->langchain) (3.4)\n",
      "Requirement already satisfied: greenlet!=0.4.17 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from SQLAlchemy<3,>=1.4->langchain) (2.0.2)\n",
      "Requirement already satisfied: packaging>=17.0 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from marshmallow<4.0.0,>=3.3.0->dataclasses-json<0.7,>=0.5.7->langchain) (23.1)\n",
      "Requirement already satisfied: mypy-extensions>=0.3.0 in /root/miniconda3/envs/langchain/lib/python3.10/site-packages (from typing-inspect>=0.4.0->dataclasses-json<0.7,>=0.5.7->langchain) (1.0.0)\n",
      "\u001b[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001b[0m\u001b[33m\n",
      "\u001b[0m"
     ]
    }
   ],
   "source": [
    "# 安装最新版本的 LangChain Python SDK（https://github.com/langchain-ai/langchain）\n",
    "!pip install -U langchain"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ce4a2474-0b69-4830-85cd-3715c22df304",
   "metadata": {},
   "source": [
    "## 模型输入 Prompts\n",
    "\n",
    "一个语言模型的提示是用户提供的一组指令或输入，用于引导模型的响应，帮助它理解上下文并生成相关和连贯的基于语言的输出，例如回答问题、完成句子或进行对话。\n",
    "\n",
    "\n",
    "- 提示模板（Prompt Templates）：参数化的模型输入\n",
    "- 示例选择器（Example Selectors）：动态选择要包含在提示中的示例\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1f14f4cf-8e30-47ab-b8b1-d58a90b5b1c1",
   "metadata": {},
   "source": [
    "## 提示模板 Prompt Templates\n",
    "\n",
    "**Prompt Templates 提供了一种预定义、动态注入、模型无关和参数化的提示词生成方式，以便在不同的语言模型之间重用模板。**\n",
    "\n",
    "一个模板可能包括指令、少量示例以及适用于特定任务的具体背景和问题。\n",
    "\n",
    "通常，提示要么是一个字符串（LLMs），要么是一组聊天消息（Chat Model）。\n",
    "\n",
    "\n",
    "类继承关系:\n",
    "\n",
    "```\n",
    "BasePromptTemplate --> PipelinePromptTemplate\n",
    "                       StringPromptTemplate --> PromptTemplate\n",
    "                                                FewShotPromptTemplate\n",
    "                                                FewShotPromptWithTemplates\n",
    "                       BaseChatPromptTemplate --> AutoGPTPrompt\n",
    "                                                  ChatPromptTemplate --> AgentScratchPadChatPromptTemplate\n",
    "\n",
    "\n",
    "\n",
    "BaseMessagePromptTemplate --> MessagesPlaceholder\n",
    "                              BaseStringMessagePromptTemplate --> ChatMessagePromptTemplate\n",
    "                                                                  HumanMessagePromptTemplate\n",
    "                                                                  AIMessagePromptTemplate\n",
    "                                                                  SystemMessagePromptTemplate\n",
    "\n",
    "PromptValue --> StringPromptValue\n",
    "                ChatPromptValue\n",
    "```\n",
    "\n",
    "\n",
    "**代码实现：https://github.com/langchain-ai/langchain/blob/master/libs/langchain/langchain/prompts**\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a40c4a43-530b-4ee1-af40-e2c90c0c8c66",
   "metadata": {},
   "source": [
    "### 使用 PromptTemplate 类生成提升词\n",
    "\n",
    "**通常，`PromptTemplate` 类的实例，使用Python的`str.format`语法生成模板化提示；也可以使用其他模板语法（例如jinja2）。**\n",
    "\n",
    "#### 使用 from_template 方法实例化 PromptTemplate"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "8d681566-cde1-4ae5-8cd7-f53cf59c3e36",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Tell me a funny joke about chickens.\n"
     ]
    }
   ],
   "source": [
    "from langchain import PromptTemplate\n",
    "\n",
    "prompt_template = PromptTemplate.from_template(\n",
    "    \"Tell me a {adjective} joke about {content}.\"\n",
    ")\n",
    "\n",
    "# 使用 format 生成提示\n",
    "prompt = prompt_template.format(adjective=\"funny\", content=\"chickens\")\n",
    "print(prompt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "f09e2b7f-d1e1-4cc3-bf9b-22de07df5513",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input_variables=['adjective', 'content'] output_parser=None partial_variables={} template='Tell me a {adjective} joke about {content}.' template_format='f-string' validate_template=True\n"
     ]
    }
   ],
   "source": [
    "print(prompt_template)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "7c329075-0a6a-4d17-9cc3-081be37f7917",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Tell me a joke\n"
     ]
    }
   ],
   "source": [
    "prompt_template = PromptTemplate.from_template(\n",
    "    \"Tell me a joke\"\n",
    ")\n",
    "# 生成提示\n",
    "prompt = prompt_template.format()\n",
    "print(prompt)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "13696d97-9c8f-4530-92ec-a9cdf2012bf7",
   "metadata": {},
   "source": [
    "#### 使用构造函数（Initializer）实例化 PromptTemplate\n",
    "\n",
    "使用构造函数实例化 `prompt_template` 时必须传入参数：`input_variables` 和 `template`。\n",
    "\n",
    "在生成提示过程中，会检查输入变量与模板字符串中的变量是否匹配，如果不匹配，则会引发异常；"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "10e7686a-dcca-4410-9263-6e941bee9c55",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "ename": "ValidationError",
     "evalue": "1 validation error for PromptTemplate\n__root__\n  Invalid prompt schema; check for mismatched or missing input parameters. 'content' (type=value_error)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mValidationError\u001b[0m                           Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[5], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m invalid_prompt \u001b[38;5;241m=\u001b[39m \u001b[43mPromptTemplate\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m      2\u001b[0m \u001b[43m    \u001b[49m\u001b[43minput_variables\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43madjective\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m      3\u001b[0m \u001b[43m    \u001b[49m\u001b[43mtemplate\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mTell me a \u001b[39;49m\u001b[38;5;132;43;01m{adjective}\u001b[39;49;00m\u001b[38;5;124;43m joke about \u001b[39;49m\u001b[38;5;132;43;01m{content}\u001b[39;49;00m\u001b[38;5;124;43m.\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\n\u001b[1;32m      4\u001b[0m \u001b[43m)\u001b[49m\n",
      "File \u001b[0;32m~/miniconda3/envs/langchain/lib/python3.10/site-packages/langchain/load/serializable.py:75\u001b[0m, in \u001b[0;36mSerializable.__init__\u001b[0;34m(self, **kwargs)\u001b[0m\n\u001b[1;32m     74\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m---> 75\u001b[0m     \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__init__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m     76\u001b[0m     \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_lc_kwargs \u001b[38;5;241m=\u001b[39m kwargs\n",
      "File \u001b[0;32m~/miniconda3/envs/langchain/lib/python3.10/site-packages/pydantic/main.py:341\u001b[0m, in \u001b[0;36mpydantic.main.BaseModel.__init__\u001b[0;34m()\u001b[0m\n",
      "\u001b[0;31mValidationError\u001b[0m: 1 validation error for PromptTemplate\n__root__\n  Invalid prompt schema; check for mismatched or missing input parameters. 'content' (type=value_error)"
     ]
    }
   ],
   "source": [
    "invalid_prompt = PromptTemplate(\n",
    "    input_variables=[\"adjective\"],\n",
    "    template=\"Tell me a {adjective} joke about {content}.\"\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6d39cd8e-99a9-4d57-aff9-0cbc673336df",
   "metadata": {},
   "source": [
    "传入 content 后才能生成可用的 prompt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "49767dc4-2057-4fee-8fb0-24436194c8d1",
   "metadata": {},
   "outputs": [],
   "source": [
    "valid_prompt = PromptTemplate(\n",
    "    input_variables=[\"adjective\", \"content\"],\n",
    "    template=\"Tell me a {adjective} joke about {content}.\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "516cee06-9673-4c9d-9499-1355034ad82a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input_variables=['adjective', 'content'] output_parser=None partial_variables={} template='Tell me a {adjective} joke about {content}.' template_format='f-string' validate_template=True\n"
     ]
    }
   ],
   "source": [
    "print(valid_prompt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "55c7a677-4ae8-4c81-829c-49fd597c45bc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Tell me a funny joke about chickens.'"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "valid_prompt.format(adjective=\"funny\", content=\"chickens\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4eeb638c-7033-4cf2-9354-6481170c9892",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "754269b1-a2a7-487e-85b3-3be916be581e",
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt_template = PromptTemplate.from_template(\n",
    "    \"讲{num}个给程序员听得笑话\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "bc708563-8556-466d-9ea5-9f41c8e6a7f9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "prompt: 讲2个给程序员听得笑话\n",
      "result: \n",
      "\n",
      "1. 两个程序员在一起，第一个程序员说：“我在搜寻一段代码，但是找不到。”第二个程序员说：“把它放在那儿，它就会自己找到了。”\n",
      "\n",
      "2. 一个程序员正在用一支笔记录他的程序，朋友问他：“你不是用电脑来写程序的吗？”他回答说：“是的，但是当我把它输入电脑之前，我想先把它写在纸上，以免出错。”\n"
     ]
    }
   ],
   "source": [
    "from langchain.llms import OpenAI\n",
    "\n",
    "llm = OpenAI(model_name=\"text-davinci-003\", max_tokens=1000)\n",
    "\n",
    "prompt = prompt_template.format(num=2)\n",
    "print(f\"prompt: {prompt}\")\n",
    "\n",
    "result = llm(prompt)\n",
    "print(f\"result: {result}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "5c6dd3ea-084a-4426-bba0-2676d42842c7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "1.两个程序员在一起，一个站起来说：“我现在要搞一个复杂系统，不知道从哪里开始。”另外一个说：“从头开始啊！” \n",
      "\n",
      "2.一个程序员去买苹果，店员问他：“要烤的还是生的？”程序员答道：“把它们烤一半，有没有半生不熟的？” \n",
      "\n",
      "3.一个程序员看到一台电脑，就问：“这台电脑多久运行一次？”店员回答：“一秒钟。”程序员说：“那我要两台！”\n"
     ]
    }
   ],
   "source": [
    "print(llm(prompt_template.format(num=3)))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1890a0b3-c5af-4d0e-b1a1-3439f3199edc",
   "metadata": {},
   "source": [
    "#### 使用 jinja2 生成模板化提示"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "4e98620d-3462-4a7d-b9ef-1a9a0fd1f496",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Tell me a funny joke about chickens'"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "jinja2_template = \"Tell me a {{ adjective }} joke about {{ content }}\"\n",
    "prompt = PromptTemplate.from_template(jinja2_template, template_format=\"jinja2\")\n",
    "\n",
    "prompt.format(adjective=\"funny\", content=\"chickens\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "3b707a3f-c6c1-4aa1-9ad2-4067c0430e26",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input_variables=['adjective', 'content'] output_parser=None partial_variables={} template='Tell me a {{ adjective }} joke about {{ content }}' template_format='jinja2' validate_template=True\n"
     ]
    }
   ],
   "source": [
    "print(prompt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "096c08d8-14ee-459e-9a5c-f0d659d08d34",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "91b48d41-608c-46e4-ac05-882b05cbfec2",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "c34c3078-3ece-4708-8bf8-cdc02968ea70",
   "metadata": {},
   "source": [
    "#### 实测：生成多种编程语言版本的快速排序"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "c73de848-8091-4ebd-9127-ea33582bad79",
   "metadata": {},
   "outputs": [],
   "source": [
    "sort_prompt_template = PromptTemplate.from_template(\n",
    "    \"生成可执行的快速排序 {programming_language} 代码\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "f3c2ee81-80aa-4ad9-b936-8c2e1b2ba27e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "def quick_sort(arr):\n",
      "    if len(arr) <= 1:\n",
      "        return arr\n",
      "\n",
      "    pivot = arr[len(arr) // 2]\n",
      "    left = [x for x in arr if x < pivot]\n",
      "    middle = [x for x in arr if x == pivot]\n",
      "    right = [x for x in arr if x > pivot]\n",
      "\n",
      "    return quick_sort(left) + middle + quick_sort(right)\n",
      "\n",
      "arr = [3,5,1,2,4]\n",
      "print(quick_sort(arr))\n"
     ]
    }
   ],
   "source": [
    "print(llm(sort_prompt_template.format(programming_language=\"python\")))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "ba3b370e-a647-4913-b4a0-c33d4dd11e35",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "public class QuickSort {\n",
      "  public static int[] sort(int[] array, int left, int right) {\n",
      "    if (left >= right) return array;\n",
      "\n",
      "    int pivot = array[left];\n",
      "    int i = left;\n",
      "    int j = right;\n",
      "\n",
      "    while (i < j) {\n",
      "      while (array[j] > pivot && i < j) {\n",
      "        j--;\n",
      "      }\n",
      "      while (array[i] <= pivot && i < j) {\n",
      "        i++;\n",
      "      }\n",
      "\n",
      "      if (i < j) {\n",
      "        int temp = array[i];\n",
      "        array[i] = array[j];\n",
      "        array[j] = temp;\n",
      "      }\n",
      "    }\n",
      "\n",
      "    array[left] = array[i];\n",
      "    array[i] = pivot;\n",
      "\n",
      "    sort(array, left, i-1);\n",
      "    sort(array, i+1, right);\n",
      "\n",
      "    return array;\n",
      "  }\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "print(llm(sort_prompt_template.format(programming_language=\"java\")))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "10ef61ce-af9f-40d6-970e-cdacc4d4736a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "#include <iostream>\n",
      "using namespace std;\n",
      "\n",
      "void quickSort(int arr[], int left, int right)\n",
      "{\n",
      "    int i = left;\n",
      "    int j = right;\n",
      "    int pivot = arr[(left + right) / 2];\n",
      " \n",
      "    while (i <= j) {\n",
      "        while (arr[i] < pivot)\n",
      "            i++;\n",
      "        while (arr[j] > pivot)\n",
      "            j--;\n",
      "        if (i <= j) {\n",
      "            swap(arr[i], arr[j]);\n",
      "            i++;\n",
      "            j--;\n",
      "        }\n",
      "    }\n",
      " \n",
      "    if (left < j)\n",
      "        quickSort(arr, left, j);\n",
      "    if (i < right)\n",
      "        quickSort(arr, i, right);\n",
      "}\n",
      "\n",
      "int main()\n",
      "{\n",
      "    int arr[10] = {3, 4, 6, 3, 5, 7, 8, 9, 1, 2};\n",
      "    quickSort(arr, 0, 9);\n",
      "    for (int i = 0; i < 10; i++)\n",
      "        cout << arr[i] << \" \";\n",
      "    cout <<endl;\n",
      "    return 0;\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "print(llm(sort_prompt_template.format(programming_language=\"C++\")))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "510954a1-2c16-414c-ada1-af9d439d43be",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "621af4b1-0337-428a-bc88-17f1ff1f876e",
   "metadata": {},
   "source": [
    "## 使用 ChatPromptTemplate 类生成适用于聊天模型的聊天记录\n",
    "\n",
    "**`ChatPromptTemplate` 类的实例，使用`format_messages`方法生成适用于聊天模型的提示。**\n",
    "\n",
    "### 使用 from_messages 方法实例化 ChatPromptTemplate"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "875c8534-7317-4111-9658-80a926458168",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.prompts import ChatPromptTemplate\n",
    "\n",
    "template = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", \"You are a helpful AI bot. Your name is {name}.\"),\n",
    "    (\"human\", \"Hello, how are you doing?\"),\n",
    "    (\"ai\", \"I'm doing well, thanks!\"),\n",
    "    (\"human\", \"{user_input}\"),\n",
    "])\n",
    "\n",
    "# 生成提示\n",
    "messages = template.format_messages(\n",
    "    name=\"Bob\",\n",
    "    user_input=\"What is your name?\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "ceb1c89b-c71a-4e7c-bf81-2f2a5ddcf80e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[SystemMessage(content='You are a helpful AI bot. Your name is Bob.', additional_kwargs={}), HumanMessage(content='Hello, how are you doing?', additional_kwargs={}, example=False), AIMessage(content=\"I'm doing well, thanks!\", additional_kwargs={}, example=False), HumanMessage(content='What is your name?', additional_kwargs={}, example=False)]\n"
     ]
    }
   ],
   "source": [
    "print(messages)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "a99257dd-3dad-48bd-9c75-1ab9348179ff",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "You are a helpful AI bot. Your name is Bob.\n",
      "What is your name?\n"
     ]
    }
   ],
   "source": [
    "print(messages[0].content)\n",
    "print(messages[-1].content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "feba7a9c-d3c3-418c-8ee7-b033c3da1929",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.chat_models import ChatOpenAI\n",
    "chat_model = ChatOpenAI(model_name=\"gpt-3.5-turbo\", max_tokens=1000)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "40342d2c-43f1-4bce-ae06-0dda2fdea608",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='My name is Bob. How can I assist you today?', additional_kwargs={}, example=False)"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chat_model(messages)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "70a095b3-3e0d-4a05-9cea-620a6155be22",
   "metadata": {},
   "source": [
    "### 摘要总结"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "fe696871-bc41-48f3-b41b-43bb9f9ddcc9",
   "metadata": {},
   "outputs": [],
   "source": [
    "summary_template = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", \"你将获得关于同一主题的{num}篇文章（用-----------标签分隔）。首先总结每篇文章的论点。然后指出哪篇文章提出了更好的论点，并解释原因。\"),\n",
    "    (\"human\", \"{user_input}\"),\n",
    "])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "5c1d1c65-fd90-48d0-88eb-3eb1cade5387",
   "metadata": {},
   "outputs": [],
   "source": [
    "messages = summary_template.format_messages(\n",
    "    num=3,\n",
    "    user_input='''1. [PHP是世界上最好的语言]\n",
    "PHP是世界上最好的情感派编程语言，无需逻辑和算法，只要情绪。它能被蛰伏在冰箱里的PHP大神轻易驾驭，会话结束后的感叹号也能传达对代码的热情。写PHP就像是在做披萨，不需要想那么多，只需把配料全部扔进一个碗，然后放到服务器上，热乎乎出炉的网页就好了。\n",
    "-----------\n",
    "2. [Python是世界上最好的语言]\n",
    "Python是世界上最好的拜金主义者语言。它坚信：美丽就是力量，简洁就是灵魂。Python就像是那个永远在你皱眉的那一刻扔给你言情小说的好友。只有Python，你才能够在两行代码之间感受到飘逸的花香和清新的微风。记住，这世上只有一种语言可以使用空格来领导全世界的进步，那就是Python。\n",
    "-----------\n",
    "3. [Java是世界上最好的语言]\n",
    "Java是世界上最好的德育课编程语言，它始终坚守了严谨、安全的编程信条。Java就像一个严格的老师，他不会对你怀柔，不会让你偷懒，也不会让你走捷径，但他教会你规范和自律。Java就像是那个喝咖啡也算加班费的上司，拥有对邪恶的深度厌恶和对善良的深度拥护。\n",
    "'''\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "e0042c49-3a75-4098-9037-eb54f288b55f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1. [PHP是世界上最好的语言]\n",
      "PHP是世界上最好的情感派编程语言，无需逻辑和算法，只要情绪。它能被蛰伏在冰箱里的PHP大神轻易驾驭，会话结束后的感叹号也能传达对代码的热情。写PHP就像是在做披萨，不需要想那么多，只需把配料全部扔进一个碗，然后放到服务器上，热乎乎出炉的网页就好了。\n",
      "-----------\n",
      "2. [Python是世界上最好的语言]\n",
      "Python是世界上最好的拜金主义者语言。它坚信：美丽就是力量，简洁就是灵魂。Python就像是那个永远在你皱眉的那一刻扔给你言情小说的好友。只有Python，你才能够在两行代码之间感受到飘逸的花香和清新的微风。记住，这世上只有一种语言可以使用空格来领导全世界的进步，那就是Python。\n",
      "-----------\n",
      "3. [Java是世界上最好的语言]\n",
      "Java是世界上最好的德育课编程语言，它始终坚守了严谨、安全的编程信条。Java就像一个严格的老师，他不会对你怀柔，不会让你偷懒，也不会让你走捷径，但他教会你规范和自律。Java就像是那个喝咖啡也算加班费的上司，拥有对邪恶的深度厌恶和对善良的深度拥护。\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(messages[-1].content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "fee264c1-941b-42b5-a737-9320eb4f5d71",
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_result = chat_model(messages)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "92ada5fc-bee1-4e66-9f87-7d454491108a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "文章1的论点是PHP是世界上最好的语言，因为它不需逻辑和算法，只需情绪来编程。文章2的论点是Python是世界上最好的语言，因为它美丽简洁，能够让人感受到飘逸的花香和清新的微风。文章3的论点是Java是世界上最好的语言，因为它严谨安全，能够教会规范和自律。\n",
      "\n",
      "其中，文章2提出了更好的论点。虽然文章1和文章3都提到了一些特点，但文章2强调了Python的美丽和简洁，并用诗意的描述表达了Python的优势。这使得文章2的论点更加有说服力和吸引力。文章1和文章3的论点相对较为平淡，没有给人留下深刻的印象。因此，文章2提出了更好的论点。\n"
     ]
    }
   ],
   "source": [
    "print(chat_result.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "dc089de4-84e1-4d50-91e5-6d49ade2cea1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[SystemMessage(content='你将获得关于同一主题的2篇文章（用-----------标签分隔）。首先总结每篇文章的论点。然后指出哪篇文章提出了更好的论点，并解释原因。', additional_kwargs={}), HumanMessage(content='1.认为“道可道”中的第一个“道”，指的是道理，如仁义礼智之类；“可道”中的“道”，指言说的意思；“常道”，指恒久存在的“道”。因此，所谓“道可道，非常道”，指的是可以言说的道理，不是恒久存在的“道”，恒久存在的“道”不可言说。如苏辙说：“莫非道也。而可道者不可常，惟不可道，而后可常耳。今夫仁义礼智，此道之可道者也。然而仁不可以为义，而礼不可以为智，可道之不可常如此。……而道常不变，不可道之能常如此。”蒋锡昌说：“此道为世人所习称之道，即今人所谓‘道理’也，第一‘道’字应从是解。《广雅·释诂》二：‘道，说也’，第二‘道’字应从是解。‘常’乃真常不易之义，在文法上为区别词。……第三‘道’字即二十五章‘道法自然’之‘道’，……乃老子学说之总名也”。陈鼓应说：“第一个‘道’字是人们习称之道，即今人所谓‘道理’。第二个‘道’字，是指言说的意思。第三个‘道’字，是老子哲学上的专有名词，在本章它意指构成宇宙的实体与动力。……‘常道’之‘常’，为真常、永恒之意。……可以用言词表达的道，就不是常道”。\\n-----------\\n2.认为“道可道”中的第一个“道”，指的是宇宙万物的本原；“可道”中的“道”，指言说的意思；“常道”，指恒久存在的“道”。因此，“道可道，非常道”，指可以言说的“道”，就不是恒久存在的“道”。如张默生说：“‘道’，指宇宙的本体而言。……‘常’，是经常不变的意思。……可以说出来的道，便不是经常不变的道”。董平说：“第一个‘道’字与‘可道’之‘道’，内涵并不相同。第一个‘道’字，是老子所揭示的作为宇宙本根之‘道’；‘可道’之‘道’，则是‘言说’的意思。……这里的大意就是说：凡一切可以言说之‘道’，都不是‘常道’或永恒之‘道’”。汤漳平等说：“第一句中的三个‘道’，第一、三均指形上之‘道’，中间的‘道’作动词，为可言之义。……道可知而可行，但非恒久不变之道”。\\n--------\\n3.认为“道可道”中的第一个“道”，指的是宇宙万物的本原；“可道”中的“道”，指言说的意思；“常道”，则指的是平常人所讲之道、常俗之道。因此，“道可道，非常道”，指“道”是可以言说的，但它不是平常人所谓的道或常俗之道。如李荣说：“道者，虚极之理也。夫论虚极之理，不可以有无分其象，不可以上下格其真。……圣人欲坦兹玄路，开以教门，借圆通之名，目虚极之理，以理可名，称之可道。故曰‘吾不知其名，字之曰道’。非常道者，非是人间常俗之道也。人间常俗之道，贵之以礼义，尚之以浮华，丧身以成名，忘己而徇利。”司马光说：“世俗之谈道者，皆曰道体微妙，不可名言。老子以为不然，曰道亦可言道耳，然非常人之所谓道也。……常人之所谓道者，凝滞于物。”裘锡圭说：“到目前为止，可以说，几乎从战国开始，大家都把‘可道’之‘道’……看成老子所否定的，把‘常道’‘常名’看成老子所肯定的。这种看法其实有它不合理的地方，……‘道’是可以说的。《老子》这个《道经》第一章，开宗明义是要讲他的‘道’。第一个‘道’字，理所应当，也是讲他要讲的‘道’：道是可以言说的。……那么这个‘恒’字应该怎么讲？我认为很简单，‘恒’字在古代作定语用，经常是‘平常’‘恒常’的意思。……‘道’是可以言说的，但是我要讲的这个‘道’，不是‘恒道’，它不是一般人所讲的‘道’。\\n', additional_kwargs={}, example=False)]\n"
     ]
    }
   ],
   "source": [
    "messages = summary_template.format_messages(\n",
    "    num=2,\n",
    "    user_input='''1.认为“道可道”中的第一个“道”，指的是道理，如仁义礼智之类；“可道”中的“道”，指言说的意思；“常道”，指恒久存在的“道”。因此，所谓“道可道，非常道”，指的是可以言说的道理，不是恒久存在的“道”，恒久存在的“道”不可言说。如苏辙说：“莫非道也。而可道者不可常，惟不可道，而后可常耳。今夫仁义礼智，此道之可道者也。然而仁不可以为义，而礼不可以为智，可道之不可常如此。……而道常不变，不可道之能常如此。”蒋锡昌说：“此道为世人所习称之道，即今人所谓‘道理’也，第一‘道’字应从是解。《广雅·释诂》二：‘道，说也’，第二‘道’字应从是解。‘常’乃真常不易之义，在文法上为区别词。……第三‘道’字即二十五章‘道法自然’之‘道’，……乃老子学说之总名也”。陈鼓应说：“第一个‘道’字是人们习称之道，即今人所谓‘道理’。第二个‘道’字，是指言说的意思。第三个‘道’字，是老子哲学上的专有名词，在本章它意指构成宇宙的实体与动力。……‘常道’之‘常’，为真常、永恒之意。……可以用言词表达的道，就不是常道”。\n",
    "-----------\n",
    "2.认为“道可道”中的第一个“道”，指的是宇宙万物的本原；“可道”中的“道”，指言说的意思；“常道”，指恒久存在的“道”。因此，“道可道，非常道”，指可以言说的“道”，就不是恒久存在的“道”。如张默生说：“‘道’，指宇宙的本体而言。……‘常’，是经常不变的意思。……可以说出来的道，便不是经常不变的道”。董平说：“第一个‘道’字与‘可道’之‘道’，内涵并不相同。第一个‘道’字，是老子所揭示的作为宇宙本根之‘道’；‘可道’之‘道’，则是‘言说’的意思。……这里的大意就是说：凡一切可以言说之‘道’，都不是‘常道’或永恒之‘道’”。汤漳平等说：“第一句中的三个‘道’，第一、三均指形上之‘道’，中间的‘道’作动词，为可言之义。……道可知而可行，但非恒久不变之道”。\n",
    "--------\n",
    "3.认为“道可道”中的第一个“道”，指的是宇宙万物的本原；“可道”中的“道”，指言说的意思；“常道”，则指的是平常人所讲之道、常俗之道。因此，“道可道，非常道”，指“道”是可以言说的，但它不是平常人所谓的道或常俗之道。如李荣说：“道者，虚极之理也。夫论虚极之理，不可以有无分其象，不可以上下格其真。……圣人欲坦兹玄路，开以教门，借圆通之名，目虚极之理，以理可名，称之可道。故曰‘吾不知其名，字之曰道’。非常道者，非是人间常俗之道也。人间常俗之道，贵之以礼义，尚之以浮华，丧身以成名，忘己而徇利。”司马光说：“世俗之谈道者，皆曰道体微妙，不可名言。老子以为不然，曰道亦可言道耳，然非常人之所谓道也。……常人之所谓道者，凝滞于物。”裘锡圭说：“到目前为止，可以说，几乎从战国开始，大家都把‘可道’之‘道’……看成老子所否定的，把‘常道’‘常名’看成老子所肯定的。这种看法其实有它不合理的地方，……‘道’是可以说的。《老子》这个《道经》第一章，开宗明义是要讲他的‘道’。第一个‘道’字，理所应当，也是讲他要讲的‘道’：道是可以言说的。……那么这个‘恒’字应该怎么讲？我认为很简单，‘恒’字在古代作定语用，经常是‘平常’‘恒常’的意思。……‘道’是可以言说的，但是我要讲的这个‘道’，不是‘恒道’，它不是一般人所讲的‘道’。\n",
    "'''\n",
    ")\n",
    "print(messages)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "c048f55c-72f7-463d-af87-9aca561ddf4b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "第一篇文章的论点是：“道可道，非常道”中的“道”指的是可以言说的道理，而不是恒久存在的道。第二篇文章的论点是：“道可道，非常道”中的“道”指的是宇宙万物的本原，而不是恒久存在的道。第三篇文章的论点是：“道可道，非常道”中的“道”指的是宇宙万物的本原，而不是平常人所谓的道或常俗之道。\n",
      "\n",
      "第二篇文章提出了更好的论点。原因如下：\n",
      "1. 第二篇文章的论点与第三篇文章的论点相似，都认为“道可道”中的第一个“道”指的是宇宙万物的本原。这与《道德经》中的思想相符，强调了“道”的宇宙本体的意义。\n",
      "2. 第一篇文章的论点将“道”解释为道理，而这种解释在第二篇和第三篇文章中没有提及。第二篇和第三篇文章更加接近《道德经》的原始意义，更符合经典文本的解读。\n",
      "3. 第二篇文章的论点更加清晰明确，没有过多的猜测和推测。它直接解释了“道可道，非常道”中的每一个词的含义，给出了合理的解释。\n",
      "\n",
      "综上所述，第二篇文章提出了更好的论点，因为它更接近《道德经》原始的意义，论证更为清晰明确。\n"
     ]
    }
   ],
   "source": [
    "chat_result = chat_model(messages)\n",
    "print(chat_result.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "211d7fea-ff97-4c92-a478-f14be0cbd00b",
   "metadata": {},
   "outputs": [],
   "source": [
    "messages = summary_template.format_messages(\n",
    "    num=2,\n",
    "    user_input='''1.认为“道可道”中的第一个“道”，指的是道理，如仁义礼智之类；“可道”中的“道”，指言说的意思；“常道”，指恒久存在的“道”。因此，所谓“道可道，非常道”，指的是可以言说的道理，不是恒久存在的“道”，恒久存在的“道”不可言说。如苏辙说：“莫非道也。而可道者不可常，惟不可道，而后可常耳。今夫仁义礼智，此道之可道者也。然而仁不可以为义，而礼不可以为智，可道之不可常如此。……而道常不变，不可道之能常如此。”蒋锡昌说：“此道为世人所习称之道，即今人所谓‘道理’也，第一‘道’字应从是解。《广雅·释诂》二：‘道，说也’，第二‘道’字应从是解。‘常’乃真常不易之义，在文法上为区别词。……第三‘道’字即二十五章‘道法自然’之‘道’，……乃老子学说之总名也”。陈鼓应说：“第一个‘道’字是人们习称之道，即今人所谓‘道理’。第二个‘道’字，是指言说的意思。第三个‘道’字，是老子哲学上的专有名词，在本章它意指构成宇宙的实体与动力。……‘常道’之‘常’，为真常、永恒之意。……可以用言词表达的道，就不是常道”。\n",
    "-----------\n",
    "2.认为“道可道”中的第一个“道”，指的是宇宙万物的本原；“可道”中的“道”，指言说的意思；“常道”，指恒久存在的“道”。因此，“道可道，非常道”，指可以言说的“道”，就不是恒久存在的“道”。如张默生说：“‘道’，指宇宙的本体而言。……‘常’，是经常不变的意思。……可以说出来的道，便不是经常不变的道”。董平说：“第一个‘道’字与‘可道’之‘道’，内涵并不相同。第一个‘道’字，是老子所揭示的作为宇宙本根之‘道’；‘可道’之‘道’，则是‘言说’的意思。……这里的大意就是说：凡一切可以言说之‘道’，都不是‘常道’或永恒之‘道’”。汤漳平等说：“第一句中的三个‘道’，第一、三均指形上之‘道’，中间的‘道’作动词，为可言之义。……道可知而可行，但非恒久不变之道”。\n",
    "'''\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "c524c96a-a754-47fd-bfc0-45aafd642d2d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "第一篇文章的论点是，“道可道”中的第一个“道”指的是道理，而“可道”中的“道”指的是言说的意思。第三个“道”指的是恒久存在的“道”。因此，“道可道，非常道”意味着可以言说的道理并非恒久存在的道。\n",
      "\n",
      "第二篇文章的论点是，“道可道”中的第一个“道”指的是宇宙万物的本原，而“可道”中的“道”指的是言说的意思。第三个“道”指的是恒久存在的道。因此，“道可道，非常道”意味着可以言说的道并非恒久存在的道。\n",
      "\n",
      "在这两篇文章中，第二篇提出了更好的论点。原因如下：\n",
      "1. 第二篇文章对于“道可道”中第一个“道”的解释更加合理。它指出这个“道”指的是宇宙的本原，而不仅仅是道理。这种解释更符合道家哲学的思想。\n",
      "2. 第二篇文章对于“可道”中的“道”指的是言说的意思的解释更加清晰。它明确指出这个“道”是指言说的意思，而不是道理。这种解释更加符合常理。\n",
      "3. 第二篇文章对于“常道”的解释更加准确。它指出“常道”指的是恒久存在的道，而不是仅仅指道理。这种解释更加符合老子学说的本质。\n",
      "\n",
      "综上所述，第二篇文章提出了更好的论点，因为它的解释更加合理、清晰，并符合道家哲学的思想。\n"
     ]
    }
   ],
   "source": [
    "chat_result = chat_model(messages)\n",
    "print(chat_result.content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "02f6280a-f38d-46f8-a5ff-06f98025f46e",
   "metadata": {},
   "source": [
    "### 使用 FewShotPromptTemplate 类生成 Few-shot Prompt \n",
    "\n",
    "构造 few-shot prompt 的方法通常有两种：\n",
    "- 从示例集（set of examples）中手动选择；\n",
    "- 通过示例选择器（Example Selector）自动选择."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "d086861b-b576-446e-bf2f-89544e500c22",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.prompts.prompt import PromptTemplate\n",
    "\n",
    "\n",
    "examples = [\n",
    "  {\n",
    "    \"question\": \"谁活得更久，穆罕默德·阿里还是艾伦·图灵？\",\n",
    "    \"answer\": \n",
    "\"\"\"\n",
    "这里需要进一步的问题吗：是的。\n",
    "追问：穆罕默德·阿里去世时多大了？\n",
    "中间答案：穆罕默德·阿里去世时74岁。\n",
    "追问：艾伦·图灵去世时多大了？\n",
    "中间答案：艾伦·图灵去世时41岁。\n",
    "所以最终答案是：穆罕默德·阿里\n",
    "\"\"\"\n",
    "  },\n",
    "  {\n",
    "    \"question\": \"craigslist的创始人是什么时候出生的？\",\n",
    "    \"answer\": \n",
    "\"\"\"\n",
    "这里需要进一步的问题吗：是的。\n",
    "追问：谁是craigslist的创始人？\n",
    "中间答案：Craigslist是由Craig Newmark创办的。\n",
    "追问：Craig Newmark是什么时候出生的？\n",
    "中间答案：Craig Newmark出生于1952年12月6日。\n",
    "所以最终答案是：1952年12月6日\n",
    "\"\"\"\n",
    "  },\n",
    "  {\n",
    "    \"question\": \"乔治·华盛顿的外祖父是谁？\",\n",
    "    \"answer\":\n",
    "\"\"\"\n",
    "这里需要进一步的问题吗：是的。\n",
    "追问：谁是乔治·华盛顿的母亲？\n",
    "中间答案：乔治·华盛顿的母亲是Mary Ball Washington。\n",
    "追问：Mary Ball Washington的父亲是谁？\n",
    "中间答案：Mary Ball Washington的父亲是Joseph Ball。\n",
    "所以最终答案是：Joseph Ball\n",
    "\"\"\"\n",
    "  },\n",
    "  {\n",
    "    \"question\": \"《大白鲨》和《皇家赌场》的导演是同一个国家的吗？\",\n",
    "    \"answer\":\n",
    "\"\"\"\n",
    "这里需要进一步的问题吗：是的。\n",
    "追问：谁是《大白鲨》的导演？\n",
    "中间答案：《大白鲨》的导演是Steven Spielberg。\n",
    "追问：Steven Spielberg来自哪里？\n",
    "中间答案：美国。\n",
    "追问：谁是《皇家赌场》的导演？\n",
    "中间答案：《皇家赌场》的导演是Martin Campbell。\n",
    "追问：Martin Campbell来自哪里？\n",
    "中间答案：新西兰。\n",
    "所以最终答案是：不是\n",
    "\"\"\"\n",
    "  }\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "9a722158-fc50-4d34-827e-224e9f6e5e7d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Question: 谁活得更久，穆罕默德·阿里还是艾伦·图灵？\n",
      "\n",
      "这里需要进一步的问题吗：是的。\n",
      "追问：穆罕默德·阿里去世时多大了？\n",
      "中间答案：穆罕默德·阿里去世时74岁。\n",
      "追问：艾伦·图灵去世时多大了？\n",
      "中间答案：艾伦·图灵去世时41岁。\n",
      "所以最终答案是：穆罕默德·阿里\n",
      "\n"
     ]
    }
   ],
   "source": [
    "example_prompt = PromptTemplate(\n",
    "    input_variables=[\"question\", \"answer\"],\n",
    "    template=\"Question: {question}\\n{answer}\"\n",
    ")\n",
    "\n",
    "# **examples[0] 是将examples[0] 字典的键值对（question-answer）解包并传递给format，作为函数参数\n",
    "print(example_prompt.format(**examples[0]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "e4b8892e-c8a8-4c58-89be-5e331f040a62",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input_variables=['question', 'answer'] output_parser=None partial_variables={} template='Question: {question}\\n{answer}' template_format='f-string' validate_template=True\n"
     ]
    }
   ],
   "source": [
    "print(example_prompt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "e0c5861c-a383-4bd5-a3ee-27258c774fd0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Question: 《大白鲨》和《皇家赌场》的导演是同一个国家的吗？\n",
      "\n",
      "这里需要进一步的问题吗：是的。\n",
      "追问：谁是《大白鲨》的导演？\n",
      "中间答案：《大白鲨》的导演是Steven Spielberg。\n",
      "追问：Steven Spielberg来自哪里？\n",
      "中间答案：美国。\n",
      "追问：谁是《皇家赌场》的导演？\n",
      "中间答案：《皇家赌场》的导演是Martin Campbell。\n",
      "追问：Martin Campbell来自哪里？\n",
      "中间答案：新西兰。\n",
      "所以最终答案是：不是\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(example_prompt.format(**examples[-1]))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "22a053e2-c8e0-4cd8-9648-01a00057d1b5",
   "metadata": {},
   "source": [
    "#### 关于解包的示例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "32806357-32c4-4e1c-ad6c-8b664bbf3004",
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true,
     "source_hidden": true
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Question: 谁活得更久，穆罕默德·阿里还是艾伦·图灵？\n",
      "Answer: \n",
      "这里需要进一步的问题吗：是的。\n",
      "追问：穆罕默德·阿里去世时多大了？\n",
      "中间答案：穆罕默德·阿里去世时74岁。\n",
      "追问：艾伦·图灵去世时多大了？\n",
      "中间答案：艾伦·图灵去世时41岁。\n",
      "所以最终答案是：穆罕默德·阿里\n",
      "\n"
     ]
    }
   ],
   "source": [
    "def print_info(question, answer):\n",
    "    print(f\"Question: {question}\")\n",
    "    print(f\"Answer: {answer}\")\n",
    "\n",
    "print_info(**examples[0]) "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "759cc0de-1065-4997-ad78-b3ad4325585c",
   "metadata": {},
   "source": [
    "### 生成 Few-shot Prompt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "ab9d0a1c-ca67-4b97-9095-011102c06046",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Question: 谁活得更久，穆罕默德·阿里还是艾伦·图灵？\n",
      "\n",
      "这里需要进一步的问题吗：是的。\n",
      "追问：穆罕默德·阿里去世时多大了？\n",
      "中间答案：穆罕默德·阿里去世时74岁。\n",
      "追问：艾伦·图灵去世时多大了？\n",
      "中间答案：艾伦·图灵去世时41岁。\n",
      "所以最终答案是：穆罕默德·阿里\n",
      "\n",
      "\n",
      "Question: craigslist的创始人是什么时候出生的？\n",
      "\n",
      "这里需要进一步的问题吗：是的。\n",
      "追问：谁是craigslist的创始人？\n",
      "中间答案：Craigslist是由Craig Newmark创办的。\n",
      "追问：Craig Newmark是什么时候出生的？\n",
      "中间答案：Craig Newmark出生于1952年12月6日。\n",
      "所以最终答案是：1952年12月6日\n",
      "\n",
      "\n",
      "Question: 乔治·华盛顿的外祖父是谁？\n",
      "\n",
      "这里需要进一步的问题吗：是的。\n",
      "追问：谁是乔治·华盛顿的母亲？\n",
      "中间答案：乔治·华盛顿的母亲是Mary Ball Washington。\n",
      "追问：Mary Ball Washington的父亲是谁？\n",
      "中间答案：Mary Ball Washington的父亲是Joseph Ball。\n",
      "所以最终答案是：Joseph Ball\n",
      "\n",
      "\n",
      "Question: 《大白鲨》和《皇家赌场》的导演是同一个国家的吗？\n",
      "\n",
      "这里需要进一步的问题吗：是的。\n",
      "追问：谁是《大白鲨》的导演？\n",
      "中间答案：《大白鲨》的导演是Steven Spielberg。\n",
      "追问：Steven Spielberg来自哪里？\n",
      "中间答案：美国。\n",
      "追问：谁是《皇家赌场》的导演？\n",
      "中间答案：《皇家赌场》的导演是Martin Campbell。\n",
      "追问：Martin Campbell来自哪里？\n",
      "中间答案：新西兰。\n",
      "所以最终答案是：不是\n",
      "\n",
      "\n",
      "Question: 玛丽·波尔·华盛顿的父亲是谁?\n"
     ]
    }
   ],
   "source": [
    "# 导入 FewShotPromptTemplate 类\n",
    "from langchain.prompts.few_shot import FewShotPromptTemplate\n",
    "\n",
    "# 创建一个 FewShotPromptTemplate 对象\n",
    "few_shot_prompt = FewShotPromptTemplate(\n",
    "    examples=examples,           # 使用前面定义的 examples 作为范例\n",
    "    example_prompt=example_prompt, # 使用前面定义的 example_prompt 作为提示模板\n",
    "    suffix=\"Question: {input}\",    # 后缀模板，其中 {input} 会被替换为实际输入\n",
    "    input_variables=[\"input\"]     # 定义输入变量的列表\n",
    ")\n",
    "\n",
    "# 使用给定的输入格式化 prompt，并打印结果\n",
    "# 这里的 {input} 将被 \"玛丽·波尔·华盛顿的父亲是谁?\" 替换\n",
    "print(few_shot_prompt.format(input=\"玛丽·波尔·华盛顿的父亲是谁?\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c559cecd-ad58-495e-92ff-765233888ad8",
   "metadata": {},
   "source": [
    "## 示例选择器 Example Selectors\n",
    "\n",
    "**如果你有大量的参考示例，就得选择哪些要包含在提示中。最好还是根据某种条件或者规则来自动选择，Example Selector 是负责这个任务的类。**\n",
    "\n",
    "BaseExampleSelector 定义如下：\n",
    "\n",
    "```python\n",
    "class BaseExampleSelector(ABC):\n",
    "    \"\"\"用于选择包含在提示中的示例的接口。\"\"\"\n",
    "\n",
    "    @abstractmethod\n",
    "    def select_examples(self, input_variables: Dict[str, str]) -> List[dict]:\n",
    "        \"\"\"根据输入选择要使用的示例。\"\"\"\n",
    "\n",
    "```\n",
    "\n",
    "`ABC` 是 Python 中的 `abc` 模块中的一个缩写，它表示 \"Abstract Base Class\"（抽象基类）。在 Python 中，抽象基类用于定义其他类必须遵循的基本接口或蓝图，但不能直接实例化。其主要目的是为了提供一种形式化的方式来定义和检查子类的接口。\n",
    "\n",
    "使用抽象基类的几点关键信息：\n",
    "\n",
    "1. **抽象方法**：在抽象基类中，你可以定义抽象方法，它没有实现（也就是说，它没有方法体）。任何继承该抽象基类的子类都必须提供这些抽象方法的实现。\n",
    "\n",
    "2. **不能直接实例化**：你不能直接创建抽象基类的实例。试图这样做会引发错误。它们的主要目的是为了被继承，并在子类中实现其方法。\n",
    "\n",
    "3. **强制子类实现**：如果子类没有实现所有的抽象方法，那么试图实例化该子类也会引发错误。这确保了继承抽象基类的所有子类都遵循了预定的接口。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "627b028b-3762-4b3d-99ca-98c88b175677",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "b8886dad-54b9-4fdf-8481-8abdd506676d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入需要的模块和类\n",
    "from langchain.prompts.example_selector import SemanticSimilarityExampleSelector\n",
    "from langchain.vectorstores import Chroma\n",
    "from langchain.embeddings import OpenAIEmbeddings\n",
    "from langchain.prompts import FewShotPromptTemplate, PromptTemplate\n",
    "\n",
    "# 定义一个提示模板\n",
    "example_prompt = PromptTemplate(\n",
    "    input_variables=[\"input\", \"output\"],     # 输入变量的名字\n",
    "    template=\"Input: {input}\\nOutput: {output}\",  # 实际的模板字符串\n",
    ")\n",
    "\n",
    "# 这是一个假设的任务示例列表，用于创建反义词\n",
    "examples = [\n",
    "    {\"input\": \"happy\", \"output\": \"sad\"},\n",
    "    {\"input\": \"tall\", \"output\": \"short\"},\n",
    "    {\"input\": \"energetic\", \"output\": \"lethargic\"},\n",
    "    {\"input\": \"sunny\", \"output\": \"gloomy\"},\n",
    "    {\"input\": \"windy\", \"output\": \"calm\"},\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "e15785f7-fa11-4b2b-a668-9838b14839ff",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 从给定的示例中创建一个语义相似性选择器\n",
    "example_selector = SemanticSimilarityExampleSelector.from_examples(\n",
    "    examples,                          # 可供选择的示例列表\n",
    "    OpenAIEmbeddings(),                # 用于生成嵌入向量的嵌入类，用于衡量语义相似性\n",
    "    Chroma,                            # 用于存储嵌入向量并进行相似性搜索的 VectorStore 类\n",
    "    k=1                                # 要生成的示例数量\n",
    ")\n",
    "\n",
    "# 创建一个 FewShotPromptTemplate 对象\n",
    "similar_prompt = FewShotPromptTemplate(\n",
    "    example_selector=example_selector,  # 提供一个 ExampleSelector 替代示例\n",
    "    example_prompt=example_prompt,      # 前面定义的提示模板\n",
    "    prefix=\"Give the antonym of every input\", # 前缀模板\n",
    "    suffix=\"Input: {adjective}\\nOutput:\",     # 后缀模板\n",
    "    input_variables=[\"adjective\"],           # 输入变量的名字\n",
    ")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "7c24dd07-c3fc-4c85-9481-a6d163723b72",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Give the antonym of every input\n",
      "\n",
      "Input: happy\n",
      "Output: sad\n",
      "\n",
      "Input: worried\n",
      "Output:\n"
     ]
    }
   ],
   "source": [
    "# 输入是一种感受，所以应该选择 happy/sad 的示例。\n",
    "print(similar_prompt.format(adjective=\"worried\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "0c4a113f-0719-4534-8932-1a363991da84",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Give the antonym of every input\n",
      "\n",
      "Input: tall\n",
      "Output: short\n",
      "\n",
      "Input: long\n",
      "Output:\n"
     ]
    }
   ],
   "source": [
    "# 输入是一种度量，所以应该选择 tall/short的示例。\n",
    "print(similar_prompt.format(adjective=\"long\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "760abbb9-c00c-43fb-8c73-341c3993f720",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Give the antonym of every input\n",
      "\n",
      "Input: windy\n",
      "Output: calm\n",
      "\n",
      "Input: rain\n",
      "Output:\n"
     ]
    }
   ],
   "source": [
    "print(similar_prompt.format(adjective=\"rain\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6da0fda7-fbef-4ded-8eb5-2a482c0475d5",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
